﻿#include "SF2Soundbank.h"
#include "collection/ArrayList.hpp"
#include "io/FileInputStream.h"
#include "lang/Boxing.h"
#include <algorithm>
using namespace yzrilyzr_collection;
using namespace yzrilyzr_io;
using namespace yzrilyzr_array;
using namespace yzrilyzr_util;
using namespace yzrilyzr_lang;
namespace yzrilyzr_soundbank{
	SF2Soundbank::SF2Soundbank()=default;
	SF2Soundbank::~SF2Soundbank(){
		delete sampleFile;
	}
	SF2Soundbank::SF2Soundbank(File & file){
		largeFormat=true;
		sampleFile=&file;
		FileInputStream is(file);
		readSoundbank(is);
	}
	void SF2Soundbank::readSoundbank(InputStream & inputstream){
		RIFFReader riff(&inputstream);
		if(riff.getFormat() != "RIFF"){
			throw Exception("Input stream is not a valid RIFF stream!");
		}
		if(riff.getType() != "sfbk"){
			throw Exception("Input stream is not a valid SoundFont!");
		}
		while(riff.hasNextChunk()){
			RIFFReader & chunk=*riff.nextChunk();
			if(chunk.getFormat() == ("LIST")){
				if(chunk.getType() == "INFO")
					readInfoChunk(chunk);
				if(chunk.getType() == "sdta")
					readSdtaChunk(chunk);
				if(chunk.getType() == "pdta")
					readPdtaChunk(chunk);
			}
		}
	}
	void SF2Soundbank::readInfoChunk(RIFFReader & riff){
		while(riff.hasNextChunk()){
			RIFFReader & chunk=*riff.nextChunk();
			String fcstr=chunk.getFormat();
			if(fcstr == "ifil"){
				major=chunk.readUnsignedShort();
				minor=chunk.readUnsignedShort();
			} else if(fcstr == "isng"){
				this->targetEngine=chunk.readString(chunk.available());
			} else if(fcstr == "INAM"){
				this->name=chunk.readString(chunk.available());
			} else if(fcstr == "irom"){
				this->romName=chunk.readString(chunk.available());
			} else if(fcstr == "iver"){
				romVersionMajor=chunk.readUnsignedShort();
				romVersionMinor=chunk.readUnsignedShort();
			} else if(fcstr == "ICRD"){
				this->creationDate=chunk.readString(chunk.available());
			} else if(fcstr == "IENG"){
				this->engineers=chunk.readString(chunk.available());
			} else if(fcstr == "IPRD"){
				this->product=chunk.readString(chunk.available());
			} else if(fcstr == "ICOP"){
				this->copyright=chunk.readString(chunk.available());
			} else if(fcstr == "ICMT"){
				this->comments=chunk.readString(chunk.available());
			} else if(fcstr == "ISFT"){
				this->tools=chunk.readString(chunk.available());
			}
		}
	}
	void SF2Soundbank::readSdtaChunk(RIFFReader & riff){
		while(riff.hasNextChunk()){
			RIFFReader & chunk=*riff.nextChunk();
			if(chunk.getFormat() == "smpl"){
				if(!largeFormat){
					std::shared_ptr<ByteArray> pSampleData=std::make_shared<ByteArray>(chunk.available());
					u_index read=0;
					u_index avail=chunk.available();
					while(read != avail){
						if(avail - read > 65536){
							chunk.readFully(*pSampleData, read, 65536);
							read+=65536;
						} else{
							chunk.readFully(*pSampleData, read, avail - read);
							read=avail;
						}
					}
					this->sampleData=std::make_shared<ModelByteBuffer>(pSampleData);
					//chunk.read(sampleData);
				} else{
					this->sampleData=std::make_shared<ModelByteBuffer>(sampleFile, chunk.getFilePointer(), chunk.available());
				}
			}
			if(chunk.getFormat() == "sm24"){
				if(!largeFormat){
					std::shared_ptr<ByteArray> pSampleData24=std::make_shared<ByteArray>(chunk.available());
					//chunk.read(sampleData24);
					u_index read=0;
					u_index avail=chunk.available();
					while(read != avail){
						if(avail - read > 65536){
							chunk.readFully(*pSampleData24, read, 65536);
							read+=65536;
						} else{
							chunk.readFully(*pSampleData24, read, avail - read);
							read=avail;
						}
					}
					this->sampleData24=std::make_shared<ModelByteBuffer>(pSampleData24);
				} else{
					this->sampleData24=std::make_shared<ModelByteBuffer>(sampleFile, chunk.getFilePointer(), chunk.available());
				}
			}
		}
	}
	void SF2Soundbank::readPdtaChunk(RIFFReader & riff){
		ArrayList<std::shared_ptr<SF2Instrument>> presets;
		ArrayList<Boxing<int32_t>> presets_bagNdx;
		ArrayList<std::shared_ptr<SF2InstrumentRegion>> presets_splits_gen;
		ArrayList<std::shared_ptr<SF2InstrumentRegion>> presets_splits_mod;
		ArrayList<std::shared_ptr<SF2Layer>> instruments;
		ArrayList<Boxing<int32_t>> instruments_bagNdx;
		ArrayList<std::shared_ptr<SF2LayerRegion>> instruments_splits_gen;
		ArrayList<std::shared_ptr<SF2LayerRegion>> instruments_splits_mod;
		while(riff.hasNextChunk()){
			RIFFReader & chunk=*riff.nextChunk();
			String format=chunk.getFormat();
			if(format == "phdr"){
				// Preset Header / Instrument
				if(chunk.available() % 38 != 0)
					throw Exception();
				u_index count=chunk.available() / 38;
				for(u_index i=0;i < count;i++){
					std::shared_ptr<SF2Instrument> preset=std::make_shared<SF2Instrument>(this);
					preset->setName(chunk.readString(20));
					preset->setPreset(chunk.readUnsignedShort());
					preset->setBank(chunk.readUnsignedShort());
					presets_bagNdx.add(chunk.readUnsignedShort());
					preset->setLibrary(chunk.readUnsignedInt());
					preset->setGenre(chunk.readUnsignedInt());
					preset->setMorphology(chunk.readUnsignedInt());
					presets.add(preset);
					if(i != count - 1)
						this->instruments.add(preset);
				}
			} else if(format == "pbag"){
				// Preset Zones / Instruments splits
				if(chunk.available() % 4 != 0)
					throw Exception();
				u_index count=chunk.available() / 4;
				// Skip first record
				{
					int32_t gencount=chunk.readUnsignedShort();
					int32_t modcount=chunk.readUnsignedShort();
					while(presets_splits_gen.size() < gencount)
						presets_splits_gen.add(nullptr);
					while(presets_splits_mod.size() < modcount)
						presets_splits_mod.add(nullptr);
					count--;
				}
				if(presets_bagNdx.isEmpty()){
					throw Exception();
				}
				Boxing<int32_t> offset=presets_bagNdx.get(0);
				// Offset should be 0 (but just case)
				for(u_index i=0;i < offset.value;i++){
					if(count == 0)
						throw Exception();
					int32_t gencount=chunk.readUnsignedShort();
					int32_t modcount=chunk.readUnsignedShort();
					while(presets_splits_gen.size() < gencount)
						presets_splits_gen.add(nullptr);
					while(presets_splits_mod.size() < modcount)
						presets_splits_mod.add(nullptr);
					count--;
				}
				for(u_index i=0;i < presets_bagNdx.size() - 1;i++){
					int32_t zone_count=(presets_bagNdx.get(i + 1) - presets_bagNdx.get(i)).value;
					SF2Instrument * preset=presets.get(i).get();
					for(int32_t ii=0;ii < zone_count;ii++){
						if(count == 0)
							throw Exception();
						int32_t gencount=chunk.readUnsignedShort();
						int32_t modcount=chunk.readUnsignedShort();
						std::shared_ptr<SF2InstrumentRegion> split=std::make_shared<SF2InstrumentRegion>();
						preset->getRegions().add(split);
						while(presets_splits_gen.size() < gencount)
							presets_splits_gen.add(split);
						while(presets_splits_mod.size() < modcount)
							presets_splits_mod.add(split);
						count--;
					}
				}
			} else if(format == "pmod"){
				// Preset Modulators / Split Modulators
				for(std::shared_ptr<SF2InstrumentRegion> instrumentRegion : presets_splits_mod){
					std::shared_ptr<SF2Modulator> modulator=std::make_shared<SF2Modulator>();
					modulator->sourceOperator=chunk.readUnsignedShort();
					modulator->destinationOperator=chunk.readUnsignedShort();
					modulator->amount=chunk.readShort();
					modulator->amountSourceOperator=chunk.readUnsignedShort();
					modulator->transportOperator=chunk.readUnsignedShort();
					SF2InstrumentRegion * split=instrumentRegion.get();
					if(split != nullptr)
						split->modulators->add(modulator);
				}
			} else if(format == "pgen"){
				// Preset Generators / Split Generators
				for(std::shared_ptr<SF2InstrumentRegion> sf2InstrumentRegion : presets_splits_gen){
					int32_t operator1=chunk.readUnsignedShort();
					int16_t amount=chunk.readShort();
					SF2InstrumentRegion * split=sf2InstrumentRegion.get();
					if(split != nullptr){
						split->generators->put(operator1, amount);
					}
				}
			} else if(format == "inst"){
				// Instrument Header / Layers
				if(chunk.available() % 22 != 0)
					throw Exception();
				u_index count=chunk.available() / 22;
				for(u_index i=0;i < count;i++){
					std::shared_ptr<SF2Layer> layer=std::make_shared<SF2Layer>(this);
					layer->setName(chunk.readString(20));
					instruments_bagNdx.add(chunk.readUnsignedShort());
					instruments.add(layer);
					if(i != count - 1)
						this->layers.add(layer);
				}
			} else if(format == "ibag"){
				// Instrument Zones / Layer splits
				if(chunk.available() % 4 != 0)
					throw Exception();
				u_index count=chunk.available() / 4;
				// Skip first record
				{
					int32_t gencount=chunk.readUnsignedShort();
					int32_t modcount=chunk.readUnsignedShort();
					while(instruments_splits_gen.size() < gencount)
						instruments_splits_gen.add(nullptr);
					while(instruments_splits_mod.size() < modcount)
						instruments_splits_mod.add(nullptr);
					count--;
				}
				if(instruments_bagNdx.isEmpty()){
					throw Exception();
				}
				Boxing<int32_t> offset=instruments_bagNdx.get(0);
				// Offset should be 0 (but just case)
				for(u_index i=0;i < offset.value;i++){
					if(count == 0)
						throw Exception();
					int32_t gencount=chunk.readUnsignedShort();
					int32_t modcount=chunk.readUnsignedShort();
					while(instruments_splits_gen.size() < gencount)
						instruments_splits_gen.add(nullptr);
					while(instruments_splits_mod.size() < modcount)
						instruments_splits_mod.add(nullptr);
					count--;
				}
				for(u_index i=0;i < instruments_bagNdx.size() - 1;i++){
					int32_t zone_count=(instruments_bagNdx.get(i + 1) - instruments_bagNdx.get(i)).value;
					std::shared_ptr<SF2Layer> layer=layers.get(i);
					for(int32_t ii=0;ii < zone_count;ii++){
						if(count == 0)
							throw Exception();
						int32_t gencount=chunk.readUnsignedShort();
						int32_t modcount=chunk.readUnsignedShort();
						std::shared_ptr<SF2LayerRegion> split=std::make_shared<SF2LayerRegion>();
						layer->getRegions().add(split);
						while(instruments_splits_gen.size() < gencount)
							instruments_splits_gen.add(split);
						while(instruments_splits_mod.size() < modcount)
							instruments_splits_mod.add(split);
						count--;
					}
				}
			} else if(format == "imod"){
				// Instrument Modulators / Split Modulators
				for(u_index i=0;i < instruments_splits_mod.size();i++){
					std::shared_ptr<SF2Modulator> modulator=std::make_shared<SF2Modulator>();
					modulator->sourceOperator=chunk.readUnsignedShort();
					modulator->destinationOperator=chunk.readUnsignedShort();
					modulator->amount=chunk.readShort();
					modulator->amountSourceOperator=chunk.readUnsignedShort();
					modulator->transportOperator=chunk.readUnsignedShort();
					if(i >= instruments_splits_gen.size()){
						throw Exception();
					}
					std::shared_ptr<SF2LayerRegion> split=instruments_splits_gen.get(i);
					if(split != nullptr)
						split->modulators->add(modulator);
				}
			} else if(format == "igen"){
				// Instrument Generators / Split Generators
				for(std::shared_ptr<SF2LayerRegion> sf2LayerRegion : instruments_splits_gen){
					int32_t operator1=chunk.readUnsignedShort();
					int16_t amount=chunk.readShort();
					SF2LayerRegion * split=sf2LayerRegion.get();
					if(split != nullptr){
						split->generators->put(operator1, amount);
					}
				}
			} else if(format == "shdr"){
				// Sample Headers
				if(chunk.available() % 46 != 0)
					throw Exception();
				u_index count=chunk.available() / 46;
				for(u_index i=0;i < count;i++){
					std::shared_ptr<SF2Sample> sample=std::make_shared<SF2Sample>(this);
					sample->setName(chunk.readString(20));
					int64_t start=chunk.readUnsignedInt();
					int64_t end=chunk.readUnsignedInt();
					if(sampleData != nullptr)
						sample->setData(sampleData->subbuffer(start * 2, end * 2, true));
					if(sampleData24 != nullptr)
						sample->setData24(sampleData24->subbuffer(start, end, true));
					/*
					sample.data = new ModelByteBuffer(sampleData, (int)(start*2),
							(int)((end - start)*2));
					if (sampleData24 != nullptr)
						sample.data24 = new ModelByteBuffer(sampleData24,
								(int)start, (int)(end - start));
					 */
					sample->setStartLoop(chunk.readUnsignedInt() - start);
					sample->setEndLoop(chunk.readUnsignedInt() - start);
					if(sample->getStartLoop() < 0)
						sample->setStartLoop(-1);
					if(sample->getEndLoop() < 0)
						sample->setEndLoop(-1);
					sample->setSampleRate(chunk.readUnsignedInt());
					sample->setOriginalPitch(chunk.readUnsignedByte());
					sample->setPitchCorrection(chunk.readByte());
					sample->setSampleLink(chunk.readUnsignedShort());
					sample->setSampleType(chunk.readUnsignedShort());
					if(i != count - 1)
						this->samples.add(sample);
				}
			}
		}
		for(auto & layer : this->layers){
			auto siter=layer->getRegions().iterator();
			std::shared_ptr<SF2LayerRegion> globalsplit=nullptr;
			while(siter->hasNext()){
				auto split=siter->next();
				if(split->generators->containsKey(SF2LayerRegion::GENERATOR_SAMPLEID)){
					auto res=split->generators->get(SF2LayerRegion::GENERATOR_SAMPLEID);
					int32_t sampleid=res.has_value()?res.value():-1;
					split->generators->remove(SF2LayerRegion::GENERATOR_SAMPLEID);
					if(sampleid < 0 || sampleid >= samples.size()){
						throw Exception();
					}
					split->setSample(samples.get(sampleid).get());
				} else{
					globalsplit=split;
				}
			}
			if(globalsplit != nullptr){
				layer->getRegions().remove(globalsplit);
				std::shared_ptr<SF2GlobalRegion> gsplit=std::make_shared<SF2GlobalRegion>();
				gsplit->generators=globalsplit->generators;
				gsplit->modulators=globalsplit->modulators;
				layer->setGlobalZone(gsplit);
			}
		}
		for(auto & instrument : this->instruments){
			auto siter=instrument->getRegions().iterator();
			std::shared_ptr<SF2InstrumentRegion> globalsplit=nullptr;
			while(siter->hasNext()){
				std::shared_ptr<SF2InstrumentRegion> split=siter->next();
				if(split->generators->containsKey(SF2LayerRegion::GENERATOR_INSTRUMENT)){
					auto res=split->generators->get(SF2LayerRegion::GENERATOR_INSTRUMENT);
					int32_t instrumentid=res.has_value()?res.value():-1;
					split->generators->remove(SF2LayerRegion::GENERATOR_INSTRUMENT);
					if(instrumentid < 0 || instrumentid >= layers.size()){
						throw Exception();
					}
					split->setLayer(layers.get(instrumentid).get());
				} else{
					globalsplit=split;
				}
			}
			if(globalsplit != nullptr){
				instrument->getRegions().remove(globalsplit);
				std::shared_ptr<SF2GlobalRegion> gsplit=std::make_shared<SF2GlobalRegion>();
				gsplit->generators=globalsplit->generators;
				gsplit->modulators=globalsplit->modulators;
				instrument->setGlobalZone(gsplit);
			}
		}
	}
	SF2Soundbank::SF2Soundbank(InputStream & inputstream){
		this->targetEngine="EMU8000";
		this->name="untitled";
		readSoundbank(inputstream);
	}
	//void SF2Soundbank::save(const String &  name) {
	//	try(RIFFWriter writer = new RIFFWriter(name, "sfbk")) {
	//		writeSoundbank(writer);
	//	}
	//}

	//void SF2Soundbank::writeSoundbank(RIFFWriter& writer) {
	//	writeInfo(writer.writeList("INFO"));
	//	writeSdtaChunk(writer.writeList("sdta"));
	//	writePdtaChunk(writer.writeList("pdta"));
	//}
	//void SF2Soundbank::writeInfo(RIFFWriter& writer) {
	//	if (this->targetEngine == nullptr)
	//		this->targetEngine = "EMU8000";
	//	if (this->name == nullptr)
	//		this->name = "";
	//	RIFFWriter ifil_chunk = writer.writeChunk("ifil");
	//	ifil_chunk.writeUnsignedShort(this->major);
	//	ifil_chunk.writeUnsignedShort(this->minor);
	//	writeInfoStringChunk(writer, "isng", this->targetEngine);
	//	writeInfoStringChunk(writer, "INAM", this->name);
	//	writeInfoStringChunk(writer, "irom", this->romName);
	//	if (romVersionMajor != -1) {
	//		RIFFWriter iver_chunk = writer.writeChunk("iver");
	//		iver_chunk.writeUnsignedShort(this->romVersionMajor);
	//		iver_chunk.writeUnsignedShort(this->romVersionMinor);
	//	}
	//	writeInfoStringChunk(writer, "ICRD", this->creationDate);
	//	writeInfoStringChunk(writer, "IENG", this->engineers);
	//	writeInfoStringChunk(writer, "IPRD", this->product);
	//	writeInfoStringChunk(writer, "ICOP", this->copyright);
	//	writeInfoStringChunk(writer, "ICMT", this->comments);
	//	writeInfoStringChunk(writer, "ISFT", this->tools);
	//	writer.close();
	//}
	//void SF2Soundbank::writeSdtaChunk(RIFFWriter writer) {
	//	byte[] pad = new byte[32];
	//	RIFFWriter smpl_chunk = writer.writeChunk("smpl");
	//	for (SF2Sample sample : samples) {
	//		ModelByteBuffer data = sample.getDataBuffer();
	//		data.writeTo(smpl_chunk);
	//		/*
	//		smpl_chunk.write(data.array(),
	//		data.arrayOffset(),
	//		data.capacity());
	//		 */
	//		smpl_chunk.write(pad);
	//		smpl_chunk.write(pad);
	//	}
	//	if (major < 2)
	//		return;
	//	if (major == 2 && minor < 4)
	//		return;
	//	for (SF2Sample sample : samples) {
	//		ModelByteBuffer data24 = sample.getData24Buffer();
	//		if (data24 == nullptr)
	//			return;
	//	}
	//	RIFFWriter sm24_chunk = writer.writeChunk("sm24");
	//	for (SF2Sample sample : samples) {
	//		ModelByteBuffer data = sample.getData24Buffer();
	//		data.writeTo(sm24_chunk);
	//		/*
	//		sm24_chunk.write(data.array(),
	//		data.arrayOffset(),
	//		data.capacity());*/
	//		smpl_chunk.write(pad);
	//	}
	//}
	//void SF2Soundbank::writePdtaChunk(RIFFWriter writer) {
	//	RIFFWriter phdr_chunk = writer.writeChunk("phdr");
	//	int phdr_zone_count = 0;
	//	for (SF2Instrument preset : this->instruments) {
	//		phdr_chunk.writeString(preset.name, 20);
	//		phdr_chunk.writeUnsignedShort(preset.preset);
	//		phdr_chunk.writeUnsignedShort(preset.bank);
	//		phdr_chunk.writeUnsignedShort(phdr_zone_count);
	//		if (preset.getGlobalRegion() != nullptr)
	//			phdr_zone_count += 1;
	//		phdr_zone_count += preset.getRegions().size();
	//		phdr_chunk.writeUnsignedInt(preset.library);
	//		phdr_chunk.writeUnsignedInt(preset.genre);
	//		phdr_chunk.writeUnsignedInt(preset.morphology);
	//	}
	//	phdr_chunk.writeString("EOP", 20);
	//	phdr_chunk.writeUnsignedShort(0);
	//	phdr_chunk.writeUnsignedShort(0);
	//	phdr_chunk.writeUnsignedShort(phdr_zone_count);
	//	phdr_chunk.writeUnsignedInt(0);
	//	phdr_chunk.writeUnsignedInt(0);
	//	phdr_chunk.writeUnsignedInt(0);
	//	RIFFWriter pbag_chunk = writer.writeChunk("pbag");
	//	int pbag_gencount = 0;
	//	int pbag_modcount = 0;
	//	for (SF2Instrument preset : this->instruments) {
	//		if (preset.getGlobalRegion() != nullptr) {
	//			pbag_chunk.writeUnsignedShort(pbag_gencount);
	//			pbag_chunk.writeUnsignedShort(pbag_modcount);
	//			pbag_gencount += preset.getGlobalRegion().getGenerators().size();
	//			pbag_modcount += preset.getGlobalRegion().getModulators().size();
	//		}
	//		for (SF2InstrumentRegion region : preset.getRegions()) {
	//			pbag_chunk.writeUnsignedShort(pbag_gencount);
	//			pbag_chunk.writeUnsignedShort(pbag_modcount);
	//			if (layers.contains(region.layer)) {
	//				// One generator is used to reference to instrument record
	//				pbag_gencount += 1;
	//			}
	//			pbag_gencount += region.getGenerators().size();
	//			pbag_modcount += region.getModulators().size();
	//		}
	//	}
	//	pbag_chunk.writeUnsignedShort(pbag_gencount);
	//	pbag_chunk.writeUnsignedShort(pbag_modcount);
	//	RIFFWriter pmod_chunk = writer.writeChunk("pmod");
	//	for (SF2Instrument preset : this->instruments) {
	//		if (preset.getGlobalRegion() != nullptr) {
	//			writeModulators(pmod_chunk,
	//				preset.getGlobalRegion().getModulators());
	//		}
	//		for (SF2InstrumentRegion region : preset.getRegions()) {
	//			writeModulators(pmod_chunk, region.getModulators());
	//		}
	//	}
	//	pmod_chunk.write(new byte[10]);
	//	RIFFWriter pgen_chunk = writer.writeChunk("pgen");
	//	for (SF2Instrument preset : this->instruments) {
	//		if (preset.getGlobalRegion() != nullptr) {
	//			writeGenerators(pgen_chunk,
	//				preset.getGlobalRegion().getGenerators());
	//		}
	//		for (SF2InstrumentRegion region : preset.getRegions()) {
	//			writeGenerators(pgen_chunk, region.getGenerators());
	//			int ix = layers.indexOf(region.layer);
	//			if (ix != -1) {
	//				pgen_chunk.writeUnsignedShort(SF2Region.GENERATOR_INSTRUMENT);
	//				pgen_chunk.writeShort((short)ix);
	//			}
	//		}
	//	}
	//	pgen_chunk.write(new byte[4]);
	//	RIFFWriter inst_chunk = writer.writeChunk("inst");
	//	int inst_zone_count = 0;
	//	for (SF2Layer instrument : this->layers) {
	//		inst_chunk.writeString(instrument.name, 20);
	//		inst_chunk.writeUnsignedShort(inst_zone_count);
	//		if (instrument.getGlobalRegion() != nullptr)
	//			inst_zone_count += 1;
	//		inst_zone_count += instrument.getRegions().size();
	//	}
	//	inst_chunk.writeString("EOI", 20);
	//	inst_chunk.writeUnsignedShort(inst_zone_count);
	//	RIFFWriter ibag_chunk = writer.writeChunk("ibag");
	//	int ibag_gencount = 0;
	//	int ibag_modcount = 0;
	//	for (SF2Layer instrument : this->layers) {
	//		if (instrument.getGlobalRegion() != nullptr) {
	//			ibag_chunk.writeUnsignedShort(ibag_gencount);
	//			ibag_chunk.writeUnsignedShort(ibag_modcount);
	//			ibag_gencount
	//				+= instrument.getGlobalRegion().getGenerators().size();
	//			ibag_modcount
	//				+= instrument.getGlobalRegion().getModulators().size();
	//		}
	//		for (SF2LayerRegion region : instrument.getRegions()) {
	//			ibag_chunk.writeUnsignedShort(ibag_gencount);
	//			ibag_chunk.writeUnsignedShort(ibag_modcount);
	//			if (samples.contains(region.sample)) {
	//				// One generator is used to reference to instrument record
	//				ibag_gencount += 1;
	//			}
	//			ibag_gencount += region.getGenerators().size();
	//			ibag_modcount += region.getModulators().size();
	//		}
	//	}
	//	ibag_chunk.writeUnsignedShort(ibag_gencount);
	//	ibag_chunk.writeUnsignedShort(ibag_modcount);
	//	RIFFWriter imod_chunk = writer.writeChunk("imod");
	//	for (SF2Layer instrument : this->layers) {
	//		if (instrument.getGlobalRegion() != nullptr) {
	//			writeModulators(imod_chunk,
	//				instrument.getGlobalRegion().getModulators());
	//		}
	//		for (SF2LayerRegion region : instrument.getRegions()) {
	//			writeModulators(imod_chunk, region.getModulators());
	//		}
	//	}
	//	imod_chunk.write(new byte[10]);
	//	RIFFWriter igen_chunk = writer.writeChunk("igen");
	//	for (SF2Layer instrument : this->layers) {
	//		if (instrument.getGlobalRegion() != nullptr) {
	//			writeGenerators(igen_chunk,
	//				instrument.getGlobalRegion().getGenerators());
	//		}
	//		for (SF2LayerRegion region : instrument.getRegions()) {
	//			writeGenerators(igen_chunk, region.getGenerators());
	//			int ix = samples.indexOf(region.sample);
	//			if (ix != -1) {
	//				igen_chunk.writeUnsignedShort(SF2Region.GENERATOR_SAMPLEID);
	//				igen_chunk.writeShort((short)ix);
	//			}
	//		}
	//	}
	//	igen_chunk.write(new byte[4]);
	//	RIFFWriter shdr_chunk = writer.writeChunk("shdr");
	//	long sample_pos = 0;
	//	for (SF2Sample sample : samples) {
	//		shdr_chunk.writeString(sample.name, 20);
	//		long start = sample_pos;
	//		sample_pos += sample.data.capacity() / 2;
	//		long end = sample_pos;
	//		long startLoop = sample.startLoop + start;
	//		long endLoop = sample.endLoop + start;
	//		if (startLoop < start)
	//			startLoop = start;
	//		if (endLoop > end)
	//			endLoop = end;
	//		shdr_chunk.writeUnsignedInt(start);
	//		shdr_chunk.writeUnsignedInt(end);
	//		shdr_chunk.writeUnsignedInt(startLoop);
	//		shdr_chunk.writeUnsignedInt(endLoop);
	//		shdr_chunk.writeUnsignedInt(sample.sampleRate);
	//		shdr_chunk.writeUnsignedByte(sample.originalPitch);
	//		shdr_chunk.writeByte(sample.pitchCorrection);
	//		shdr_chunk.writeUnsignedShort(sample.sampleLink);
	//		shdr_chunk.writeUnsignedShort(sample.sampleType);
	//		sample_pos += 32;
	//	}
	//	shdr_chunk.writeString("EOS", 20);
	//	shdr_chunk.write(new byte[26]);
	//}
	//void SF2Soundbank::writeInfoStringChunk(RIFFWriter writer, const String &  name,
	//	const String &  value) {
	//	if (value == nullptr)
	//		return;
	//	RIFFWriter chunk = writer.writeChunk(name);
	//	chunk.writeString(value);
	//	int len = value.getBytes(US_ASCII).length;
	//	chunk.write(0);
	//	len++;
	//	if (len % 2 != 0)
	//		chunk.write(0);
	//}
	//void SF2Soundbank::writeModulators(RIFFWriter writer, List<SF2Modulator> modulators)
	//{
	//	for (SF2Modulator modulator : modulators) {
	//		writer.writeUnsignedShort(modulator.sourceOperator);
	//		writer.writeUnsignedShort(modulator.destinationOperator);
	//		writer.writeShort(modulator.amount);
	//		writer.writeUnsignedShort(modulator.amountSourceOperator);
	//		writer.writeUnsignedShort(modulator.transportOperator);
	//	}
	//}
	//void SF2Soundbank::writeGenerators(RIFFWriter writer, Map<Integer, Short> generators)
	//{
	//	Short keyrange = generators.get(SF2Region.GENERATOR_KEYRANGE);
	//	Short velrange = generators.get(SF2Region.GENERATOR_VELRANGE);
	//	if (keyrange != nullptr) {
	//		writer.writeUnsignedShort(SF2Region.GENERATOR_KEYRANGE);
	//		writer.writeShort(keyrange);
	//	}
	//	if (velrange != nullptr) {
	//		writer.writeUnsignedShort(SF2Region.GENERATOR_VELRANGE);
	//		writer.writeShort(velrange);
	//	}
	//	for (Map.Entry<Integer, Short> generator : generators.entrySet()) {
	//		if (generator.getKey() == SF2Region.GENERATOR_KEYRANGE)
	//			continue;
	//		if (generator.getKey() == SF2Region.GENERATOR_VELRANGE)
	//			continue;
	//		writer.writeUnsignedShort(generator.getKey());
	//		writer.writeShort(generator.getValue());
	//	}
	//}

	//void SF2Soundbank::save(File* file) {
	//	try(RIFFWriter writer = new RIFFWriter(file, "sfbk")) {
	//		writeSoundbank(writer);
	//	}
	//}
	//void SF2Soundbank::save(OutputStream* out) {
	//	try(RIFFWriter writer = new RIFFWriter(out, "sfbk")) {
	//		writeSoundbank(writer);
	//	}
	//}
	String SF2Soundbank::getName()const{
		return name;
	}
	void SF2Soundbank::setName(const String & s){
		name=s;
	}
	String SF2Soundbank::getVersion()const{
		return std::to_string(major) + "." + std::to_string(minor);
	}
	String SF2Soundbank::getVendor()const{
		return engineers;
	}
	void SF2Soundbank::setVendor(const String & s){
		engineers=s;
	}
	String SF2Soundbank::getDescription()const{
		return comments;
	}
	void SF2Soundbank::setDescription(const String & s){
		comments=s;
	}
	std::vector<std::shared_ptr<SoundbankResource>> SF2Soundbank::getResources(){
		auto resources=std::vector<std::shared_ptr<SoundbankResource>>();
		for(auto & layer : layers){ resources.push_back(layer); }
		for(auto & sample : samples){ resources.push_back(sample); }
		return resources;
	}
	bool compareInstrument(std::shared_ptr<SF2Instrument> arg0, std::shared_ptr<SF2Instrument> arg1){
		int a=arg0->getBank() * 128 + arg0->getPreset();
		int b=arg1->getBank() * 128 + arg1->getPreset();
		a+=arg0->getBank() == 128?2097152:0;
		b+=arg1->getBank() == 128?2097152:0;
		return a - b > 0;
	}
	std::vector<std::shared_ptr<Instrument>> SF2Soundbank::getInstruments(){
		auto arr=std::vector<std::shared_ptr<Instrument>>();
		for(auto & obj : instruments)arr.push_back(obj);
		return arr;
	}
	std::shared_ptr<Instrument> SF2Soundbank::getInstrument(int32_t program, int32_t bank){
		for(auto & instrument : instruments){
			int program2=instrument->getPreset();
			int bank2=instrument->getBank();
			if(program == program2 && bank == bank2){
				return instrument;
			}
		}
		return nullptr;
	}
	Array<std::shared_ptr<SF2Layer>> SF2Soundbank::getLayers(){
		return layers.toArray();
	}
	Array<std::shared_ptr<SF2Sample>> SF2Soundbank::getSamples(){
		return samples.toArray();
	}
	String SF2Soundbank::getCreationDate(){
		return creationDate;
	}
	void SF2Soundbank::setCreationDate(const String & creationDate){
		this->creationDate=creationDate;
	}
	String SF2Soundbank::getProduct(){
		return product;
	}
	void SF2Soundbank::setProduct(const String & product){
		this->product=product;
	}
	String SF2Soundbank::getRomName(){
		return romName;
	}
	void SF2Soundbank::setRomName(const String & romName){
		this->romName=romName;
	}
	int SF2Soundbank::getRomVersionMajor(){
		return romVersionMajor;
	}
	void SF2Soundbank::setRomVersionMajor(int romVersionMajor){
		this->romVersionMajor=romVersionMajor;
	}
	int SF2Soundbank::getRomVersionMinor(){
		return romVersionMinor;
	}
	void SF2Soundbank::setRomVersionMinor(int romVersionMinor){
		this->romVersionMinor=romVersionMinor;
	}
	String SF2Soundbank::getTargetEngine(){
		return targetEngine;
	}
	void SF2Soundbank::setTargetEngine(const String & targetEngine){
		this->targetEngine=targetEngine;
	}
	String SF2Soundbank::getTools(){
		return tools;
	}
	void SF2Soundbank::setTools(const String & tools){
		this->tools=tools;
	}
	void SF2Soundbank::addResource(std::shared_ptr <SoundbankResource >resource){
		if(auto paramRegPtr=std::dynamic_pointer_cast<SF2Instrument>(resource))
			instruments.add(paramRegPtr);
		if(auto paramRegPtr=std::dynamic_pointer_cast<SF2Layer>(resource))
			layers.add(paramRegPtr);
		if(auto paramRegPtr=std::dynamic_pointer_cast<SF2Sample>(resource))
			samples.add(paramRegPtr);
	}
	ModelByteBuffer * SF2Soundbank::getSampleData(){
		return sampleData.get();
	}
	void SF2Soundbank::removeResource(std::shared_ptr < SoundbankResource > resource){
		if(auto paramRegPtr=std::dynamic_pointer_cast<SF2Instrument>(resource))
			instruments.remove(paramRegPtr);
		if(auto paramRegPtr=std::dynamic_pointer_cast<SF2Layer>(resource))
			layers.remove(paramRegPtr);
		if(auto paramRegPtr=std::dynamic_pointer_cast<SF2Sample>(resource))
			samples.remove(paramRegPtr);
	}
	void SF2Soundbank::addInstrument(std::shared_ptr < SF2Instrument >resource){
		instruments.add(resource);
	}
	void SF2Soundbank::removeInstrument(std::shared_ptr < SF2Instrument > resource){
		instruments.remove(resource);
	}
}